const std = @import("std");
const builtin = @import("builtin");

pub const Compare = enum {
    Equal, // 相等
    Greater, // 大于
    Less, // 小于
};

pub const String = struct {
    buffer: ?[]u8,
    use_size: usize,
    allocator: std.mem.Allocator,

    pub fn init(allocator: std.mem.Allocator) String {
        return .{
            .buffer = null,
            .allocator = allocator,
            .use_size = 0,
        };
    }

    pub fn deinit(self: String) void {
        if (self.buffer) |buffer| {
            self.allocator.free(buffer);
        }
    }

    pub fn capacity(self: String) usize {
        if (self.buffer) |buffer| {
            return buffer.len;
        }
        return 0;
    }

    pub fn size(self: String) usize {
        return self.use_size;
    }

    pub fn isEmpty(self: String) bool {
        return self.use_size == 0;
    }

    pub fn clear(self: *String) void {
        if (self.capacity() == 0) {
            return;
        } else {
            if (self.buffer) |buffer| {
                @memset(buffer, 0);
            }
            self.use_size = 0;
        }
    }

    pub fn debugPrint(self: String, line_num: usize) void {
        if (self.buffer) |buff| {
            std.debug.print("字符串中长:{d}\n", .{self.capacity()});
            for (0..self.capacity()) |i| {
                if (i != 0 and i % line_num == 0) {
                    std.debug.print("\n", .{});
                }
                if (buff[i] == 0) {
                    std.debug.print("{d} ", .{buff[i]});
                } else if (buff[i] >= 127) {
                    std.debug.print("{x} ", .{buff[i]});
                } else {
                    std.debug.print("{c} ", .{buff[i]});
                }
            }
            std.debug.print("\n", .{});
        }
    }

    /// len 指定需要的大小
    pub fn setNeedSize(self: *String, len: usize) !void {
        var maxSize: usize = self.capacity();
        const freeSize = maxSize - self.size();
        if (freeSize > len) {
            return;
        }
        maxSize = self.size() + len + 1;
        if (self.buffer) |buffer| {
            self.buffer = self.allocator.realloc(buffer, maxSize) catch {
                return error.IceMemoryError;
            };
            if (self.buffer) |buff| {
                for (self.use_size..maxSize) |i| {
                    buff[i] = 0;
                }
            }
        } else {
            self.buffer = self.allocator.alloc(u8, maxSize) catch {
                return error.IceMemoryError;
            };
            if (self.buffer) |buffer| {
                @memset(buffer, 0);
            }
        }
    }

    pub fn pushBack(self: *String, comptime T: type, val: T) !void {
        switch (@typeInfo(T)) {
            .Int, .Float => {
                const temp = std.fmt.allocPrint(self.allocator, "{d}", .{val}) catch {
                    return error.IceMemoryError;
                };
                defer self.allocator.free(temp);
                try self.pushBackCharArray(temp);
            },
            .Pointer => |typeInfo| {
                if (typeInfo.child == u8) {
                    try self.pushBackCharArray(val);
                } else {
                    std.debug.print("类型:{any}\n", .{T});
                    return error.IceTypeCastCharError;
                }
            },
            else => {
                std.debug.print("类型:{any}\n", .{T});
                return error.IceTypeCastCharError;
            },
        }
    }

    pub fn pushBackChar(self: *String, val: u8) !void {
        try self.setNeedSize(1);
        if (self.buffer) |buffer| {
            buffer[self.use_size] = val;
            self.use_size += 1;
        }
    }

    pub fn pushBackCharArray(self: *String, val: []const u8) !void {
        try self.setNeedSize(val.len);
        if (self.buffer) |buffer| {
            var old_size = self.size();
            for (val) |ch| {
                if (ch == 0) {
                    break;
                }
                buffer[old_size] = ch;
                old_size += 1;
            }
            self.use_size = old_size;
        } else {
            return error.IceMemoryError;
        }
    }

    pub fn pushBackStr(self: *String, val: String) !void {
        try self.pushBackCharArray(val.toStr());
    }

    pub fn pushBackFormat(self: *String, comptime format: []const u8, args: anytype) !void {
        const temp = try std.fmt.allocPrint(self.allocator, format, args);
        defer self.allocator.free(temp);
        try self.pushBackCharArray(temp);
    }

    pub fn insertCharArray(self: *String, pos: usize, val: []const u8) !void {
        try self.setNeedSize(val.len);
        if (pos >= self.size()) {
            try self.pushBackCharArray(val);
            return;
        }
        if (self.buffer) |buffer| {
            var last_pos = self.use_size + val.len - 1;
            var temp: usize = 1;
            while (last_pos >= pos + val.len) {
                buffer[last_pos] = buffer[self.use_size - temp];
                last_pos -= 1;
                temp += 1;
            }

            for (val, 0..) |ch, i| {
                buffer[pos + i] = ch;
            }
            self.use_size += val.len;
        } else {
            return error.IceMemoryError;
        }
    }

    pub fn insertChar(self: *String, pos: usize, val: u8) !void {
        try self.setNeedSize(1);
        if (pos >= self.size()) {
            try self.pushBackChar(val);
            return;
        }
        if (self.buffer) |buffer| {
            var i: usize = self.use_size;
            while (true) {
                buffer[i] = buffer[i - 1];
                if (i == pos) {
                    buffer[i] = val;
                    break;
                }
                i -= 1;
                if (i == 0) {
                    break;
                }
            }
            self.use_size += 1;
        } else {
            return error.IceMemoryError;
        }
    }

    pub fn insert(self: *String, comptime T: type, pos: usize, val: T) !void {
        switch (@typeInfo(T)) {
            .Int, .Float => {
                const temp = std.fmt.allocPrint(self.allocator, "{d}", .{val}) catch {
                    return error.IceMemoryError;
                };
                defer self.allocator.free(temp);
                try self.insertCharArray(pos, temp);
            },
            .Pointer => |typeInfo| {
                if (typeInfo.child == u8) {
                    try self.insertCharArray(pos, val);
                } else {
                    std.debug.print("类型:{any}\n", .{T});
                    return error.IceTypeCastCharError;
                }
            },
            else => {
                std.debug.print("类型:{any}\n", .{T});
                return error.IceTypeCastCharError;
            },
        }
    }

    pub fn insertFormat(self: *String, pos: usize, comptime format: []const u8, args: anytype) !void {
        const temp = try std.fmt.allocPrint(self.allocator, format, args);
        defer self.allocator.free(temp);
        try self.insertCharArray(pos, temp);
    }

    pub fn toStr(self: String) []const u8 {
        if (self.buffer) |buffer| {
            return buffer[0..self.use_size];
        } else {
            return "";
        }
    }

    pub fn data(self: String) ![]u8 {
        if (self.buffer) |buffer| {
            return buffer;
        } else {
            return error.IceEmptyDataError;
        }
    }

    pub fn updateUseSize(self: *String) void {
        if (self.buffer) |buffer| {
            self.use_size = 0;
            for (buffer) |ch| {
                if (ch == 0) {
                    break;
                }
                self.use_size += 1;
            }
        } else {
            self.use_size = 0;
        }
    }

    pub fn equal(self: String, str: []const u8) bool {
        if (self.size() != str.len) {
            return false;
        }
        if (self.buffer) |buffer| {
            return std.mem.eql(u8, buffer[0..self.size()], str);
        } else {
            return false;
        }
    }

    pub fn equalIgnoreCase(self: String, str: []const u8) bool {
        if (self.size() != str.len) {
            return false;
        }
        if (self.buffer) |buffer| {
            return std.ascii.eqlIgnoreCase(buffer[0..self.size()], str);
        } else {
            return false;
        }
    }

    pub fn clone(self: String) !String {
        var ret = String.init(self.allocator);
        errdefer ret.deinit();

        if (self.capacity() > 0) {
            try ret.setNeedSize(self.capacity() - 1);
        }
        if (self.use_size > 0) {
            try ret.pushBackCharArray(self.toStr());
        }
        return ret;
    }

    pub fn lower(self: *String) void {
        if (self.buffer) |buffer| {
            for (0..self.use_size) |i| {
                buffer[i] = std.ascii.toLower(buffer[i]);
            }
        }
    }

    pub fn toLower(self: String) !String {
        var ret = try self.clone();
        ret.lower();
        return ret;
    }

    pub fn upper(self: *String) void {
        if (self.buffer) |buffer| {
            for (0..self.use_size) |i| {
                buffer[i] = std.ascii.toUpper(buffer[i]);
            }
        }
    }

    pub fn toUpper(self: String) !String {
        var ret = try self.clone();
        ret.upper();
        return ret;
    }

    /// pos >=0 表示数组的下标
    /// pos < 0 -1 数组倒数第一个元素 -2 倒数第二个元素 ...
    pub fn at(self: String, pos: i64) !u8 {
        const newPos = try self.i64ToUsize(pos);
        if (self.buffer) |buffer| {
            return buffer[newPos];
        }
        return error.IceArrayIndexError;
    }

    fn i64ToUsize(self: String, pos: i64) !usize {
        if (pos < 0) {
            var newPos = self.use_size;
            var oldPos = pos;
            while (oldPos < 0) {
                newPos -= 1;
                oldPos += 1;
                if (oldPos >= 0) {
                    break;
                }
                if (newPos == 0) {
                    return error.IceArrayIndexError;
                }
            }
            return newPos;
        } else {
            if (pos < 0) {
                return error.CastUsizeError;
            }
            const newPos: usize = std.math.cast(usize, pos) orelse return error.CastUsizeError;
            if (newPos >= self.use_size) {
                return error.IceArrayIndexError;
            }
            return newPos;
        }
    }

    pub fn isInteger(self: String) bool {
        if (self.buffer) |buffer| {
            for (buffer) |ch| {
                switch (ch) {
                    48...57 => {},
                    else => {
                        return false;
                    },
                }
            }
        } else {
            return false;
        }
        return true;
    }

    pub fn isNull(self: String) bool {
        if (self.buffer) {
            return self.equalIgnoreCase("null");
        } else {
            return false;
        }
    }

    pub fn toInteger(self: String, comptime T: type, radix: u8) !T {
        return std.fmt.parseInt(T, self.toStr(), radix);
        // if (self.isInteger()) {
        //     return std.fmt.parseInt(T, self.toStr(), radix);
        // } else {
        //     return error.IceToIntegerError;
        // }
    }

    pub fn toFloat(self: String, comptime T: type) !T {
        return std.fmt.parseFloat(T, self.toStr());
    }

    pub fn isFloat(self: String) bool {
        if (self.buffer) |buffer| {
            var findDot = false;
            var findE = false;
            for (buffer) |ch| {
                switch (ch) {
                    48...57 => {},
                    46 => {
                        if (findE) {
                            return false;
                        }
                        if (findDot) {
                            return false;
                        } else {
                            findDot = true;
                        }
                    },
                    69, 101 => {
                        if (findDot) {
                            return false;
                        }
                        if (findE) {
                            return false;
                        } else {
                            findE = true;
                        }
                    },
                    else => {
                        return false;
                    },
                }
            }
        } else {
            return false;
        }
        return true;
    }

    pub fn isBool(self: String) bool {
        if (self.equalIgnoreCase("true") or self.equalIgnoreCase("false")) {
            return true;
        } else {
            return false;
        }
    }

    pub fn compareCharArray(self: String, rightStr: []const u8) Compare {
        return compareBuffer(self.toStr(), rightStr);
    }

    pub fn compareStr(self: String, rightStr: String) Compare {
        return compareBuffer(self.toStr(), rightStr.toStr());
    }

    pub fn popBack(self: *String) !u8 {
        if (self.size() == 0) {
            return error.IceEmptyDataError;
        }
        if (self.buffer) |buffer| {
            const ch = buffer[self.use_size - 1];
            buffer[self.use_size - 1] = 0;
            self.use_size -= 1;
            return ch;
        } else {
            return error.IceEmptyDataError;
        }
    }
    pub fn popBackWord(self: *String) !String {
        if (self.size() == 0) {
            return error.IceEmptyDataError;
        }
        if (self.buffer) |buffer| {
            var len: usize = 0;
            var i: usize = 0;
            while (i < self.use_size) {
                const wordLen = getUtf8Size(buffer[i]);
                if (i + wordLen == self.use_size) {
                    return try self.popBackSize(wordLen);
                }
                len += 1;
                i += wordLen;
            }
            return error.IceAssertError;
        } else {
            return error.IceEmptyDataError;
        }
    }
    pub fn popBackSize(self: *String, len: usize) !String {
        if (self.size() == 0) {
            return error.IceEmptyDataError;
        }
        if (self.size() < len) {
            // 数据太少无法删除指定的大小
            return error.IceDataLessError;
        }
        if (self.buffer) |buffer| {
            var ret = String.init(self.allocator);
            errdefer ret.deinit();
            try ret.setNeedSize(len);
            const start = self.use_size - len;
            for (start..self.use_size) |i| {
                try ret.pushBackChar(buffer[i]);
                buffer[i] = 0;
            }
            self.use_size -= len;
            return ret;
        } else {
            return error.IceEmptyDataError;
        }
    }

    pub fn popFront(self: *String) !u8 {
        if (self.size() == 0) {
            return error.IceEmptyDataError;
        }
        if (self.buffer) |buffer| {
            const end = self.use_size - 1;
            const ch = buffer[0];
            for (0..end) |i| {
                buffer[i] = buffer[i + 1];
            }
            buffer[end] = 0;
            self.use_size -= 1;
            return ch;
        } else {
            return error.IceEmptyDataError;
        }
    }

    pub fn popFrontWord(self: *String) !String {
        if (self.size() == 0) {
            return error.IceEmptyDataError;
        }
        if (self.buffer) |buffer| {
            const len = getUtf8Size(buffer[0]);
            return try self.popFrontSize(len);
        } else {
            return error.IceEmptyDataError;
        }
    }
    pub fn popFrontSize(self: *String, len: usize) !String {
        if (self.size() == 0) {
            return error.IceEmptyDataError;
        }
        if (self.size() < len) {
            // 数据太少无法删除指定的大小
            return error.IceDataLessError;
        } else if (self.size() == len) {
            const ret = try self.clone();
            self.clear();
            return ret;
        }
        if (self.buffer) |buffer| {
            var ret = String.init(self.allocator);
            errdefer ret.deinit();
            try ret.setNeedSize(len);
            const end = self.use_size - len;
            for (0..end) |i| {
                if (ret.size() < len) {
                    try ret.pushBackChar(buffer[i]);
                }
                buffer[i] = buffer[i + len];
            }
            self.use_size = self.use_size - len;
            for (0..len) |i| {
                buffer[self.use_size + i] = 0;
            }
            return ret;
        } else {
            return error.IceEmptyDataError;
        }
    }

    pub fn replace(self: *String, oldStr: []const u8, newStr: []const u8) !void {
        if (oldStr.len == newStr.len and std.mem.eql(u8, oldStr, newStr)) {
            return;
        }

        if (self.find(oldStr)) |newPos| {
            try self.remove(newPos, oldStr.len);
            try self.insertCharArray(newPos, newStr);
        }
    }

    pub fn replaceAll(self: *String, oldStr: []const u8, newStr: []const u8) !void {
        if (oldStr.len == newStr.len and std.mem.eql(u8, oldStr, newStr)) {
            return;
        }
        while (true) {
            if (self.find(oldStr)) |newPos| {
                try self.remove(newPos, oldStr.len);
                try self.insertCharArray(newPos, newStr);
            } else {
                break;
            }
        }
    }
    pub fn swapChar(self: *String, pos: i64, newCh: u8) !void {
        if (self.buffer) |buffer| {
            const new_pos = try self.i64ToUsize(pos);
            buffer[new_pos] = newCh;
            return;
        }

        return error.IceArrayIndexError;
    }

    pub fn remove(self: *String, pos: usize, len: usize) !void {
        if (self.size() == 0) {
            return error.IceEmptyDataError;
        }
        if (self.buffer) |buffer| {
            var i = pos;
            while (i < self.use_size) {
                if ((i + len) > self.use_size) {
                    break;
                }
                buffer[i] = buffer[i + len];
                i += 1;
            }
            self.use_size = self.use_size - len;
            return;
        }
        return error.IceEmptyDataError;
    }

    pub fn wordSize(self: String) usize {
        if (self.buffer) |buffer| {
            var len: usize = 0;
            var i: usize = 0;
            while (i < self.use_size) {
                i += getUtf8Size(buffer[i]);
                len += 1;
            }
            return len;
        } else {
            return 0;
        }
    }
    pub fn find(self: String, str: []const u8) ?usize {
        return std.mem.indexOf(u8, self.toStr(), str);
    }
    pub fn findPos(self: String, startPos: usize, str: []const u8) ?usize {
        if (startPos >= self.use_size) {
            return null;
        }
        if (self.buffer) |buffer| {
            const pos = std.mem.indexOf(u8, buffer[startPos..self.use_size], str);
            if (pos) |temp| {
                return startPos + temp;
            } else {
                return null;
            }
        } else {
            return null;
        }
    }
    pub fn lastFind(self: String, str: []const u8) ?usize {
        if (self.buffer) |_| {
            return std.mem.lastIndexOf(u8, self.toStr(), str);
        } else {
            return null;
        }
    }
    pub fn trimLeft(self: *String) !void {
        if (self.buffer) |buffer| {
            var len: usize = 0;
            for (0..self.use_size) |i| {
                if (std.ascii.isWhitespace(buffer[i])) {
                    len += 1;
                } else {
                    break;
                }
            }
            if (len > 0) {
                try self.remove(0, len);
            }
        }
    }
    pub fn trimRight(self: *String) !void {
        if (self.buffer) |buffer| {
            var len: usize = 0;
            var pos = self.use_size - 1;
            while (true) {
                if (std.ascii.isWhitespace(buffer[pos])) {
                    len += 1;
                    if (pos == 0) {
                        break;
                    }
                    pos -= 1;
                } else {
                    break;
                }
            }
            if (len > 0) {
                try self.remove(pos, len);
            }
        }
    }
    pub fn trim(self: *String) !void {
        try self.trimRight();
        try self.trimLeft();
    }

    pub fn containsCharArray(self: String, str: []const u8) bool {
        if (self.find(str)) |_| {
            return true;
        }
        return false;
    }

    pub fn containsStr(self: String, str: String) bool {
        return self.containsCharArray(str.toStr());
    }

    pub fn split(self: String, str: []const u8) std.ArrayList(String) {
        var ret_list = std.ArrayList(String).init(self.allocator);
        var pos: usize = 0;
        while (true) {
            if (self.findPos(pos, str)) |newPos| {
                var item: String = String.init(self.allocator);
                if (self.buffer) |buffer| {
                    item.pushBackCharArray(buffer[pos..newPos]) catch {
                        return ret_list;
                    };
                }
                ret_list.append(item) catch {
                    return ret_list;
                };
                pos = newPos + str.len;
                if (pos >= self.use_size) {
                    break;
                }
            } else {
                var item: String = String.init(self.allocator);
                if (self.buffer) |buffer| {
                    item.pushBackCharArray(buffer[pos..self.use_size]) catch {
                        return ret_list;
                    };
                }
                ret_list.append(item) catch {
                    return ret_list;
                };
                break;
            }
        }
        return ret_list;
    }

    pub fn splitNum(self: String, str: []const u8, total: usize) std.ArrayList(String) {
        var retList = std.ArrayList(String).init(self.allocator);
        if (total == 1) {
            const item = self.clone() catch {
                return retList;
            };
            retList.append(item) catch {
                return retList;
            };
            return retList;
        }
        var pos: usize = 0;
        while (true) {
            if (self.findPos(pos, str)) |newPos| {
                var item: String = String.init(self.allocator);
                errdefer item.deinit();
                if (self.buffer) |buffer| {
                    item.pushBackCharArray(buffer[pos..newPos]) catch {
                        return retList;
                    };
                }
                retList.append(item) catch {
                    return retList;
                };
                pos = newPos + str.len;
                if (pos >= self.use_size or retList.items.len + 1 == total) {
                    break;
                }
            } else {
                var item: String = String.init(self.allocator);
                errdefer item.deinit();
                if (self.buffer) |buffer| {
                    item.pushBackCharArray(buffer[pos..self.use_size]) catch {
                        return retList;
                    };
                }
                retList.append(item) catch {
                    return retList;
                };
                pos = self.use_size;
                break;
            }
        }
        if (pos < self.use_size) {
            var item: String = String.init(self.allocator);
            errdefer item.deinit();
            if (self.buffer) |buffer| {
                item.pushBackCharArray(buffer[pos..self.use_size]) catch {
                    return retList;
                };
            }
            retList.append(item) catch {
                return retList;
            };
            pos = self.use_size;
        }
        return retList;
    }

    pub fn toLines(self: String) std.ArrayList(String) {
        return self.split("\n");
    }

    pub fn subStr(self: String, startPos: usize, len: usize) String {
        var ret = String.init(self.allocator);
        if (startPos > self.use_size) {
            return ret;
        }
        if (self.buffer) |buffer| {
            for (startPos..self.use_size) |i| {
                ret.pushBackChar(buffer[i]) catch {
                    return ret;
                };
                if (ret.size() == len) {
                    break;
                }
            }
        }
        return ret;
    }

    pub fn startWith(self: String, str: []const u8) bool {
        if (str.len == 0) {
            return false;
        }
        if (self.find(str)) |pos| {
            return pos == 0;
        }
        return false;
    }
    pub fn startWithIgnoreCase(self: String, str: []const u8) bool {
        if (str.len == 0) {
            return false;
        }
        return std.ascii.startsWithIgnoreCase(self.toStr(), str);
    }
    pub fn endWith(self: String, str: []const u8) bool {
        if (str.len == 0) {
            return false;
        }
        if (self.lastFind(str)) |pos| {
            return (pos + str.len) == self.use_size;
        }
        return false;
    }
    pub fn endWithIgnoreCase(self: String, str: []const u8) bool {
        if (str.len == 0) {
            return false;
        }
        return std.ascii.endsWithIgnoreCase(self.toStr(), str);
    }
    pub fn getWord(self: String, pos: usize) !String {
        if (self.size() == 0) {
            return error.IceEmptyDataError;
        }
        if (self.buffer) |buffer| {
            var len: usize = 0;
            var i: usize = 0;
            while (i < self.use_size) {
                const wordLen = getUtf8Size(buffer[i]);
                if (pos == len) {
                    var ret = String.init(self.allocator);
                    try ret.pushBackCharArray(buffer[i..(i + wordLen)]);
                    return ret;
                }
                len += 1;
                i += wordLen;
            }
            return error.IceNotFindError;
        } else {
            return error.IceEmptyDataError;
        }
    }
};

inline fn getUtf8Size(char: u8) u8 {
    return std.unicode.utf8ByteSequenceLength(char) catch {
        return 1;
    };
}

/// 比较字符串大小 根据ascii码判断
/// 0 字符串相等
/// 1 大于 charStr
/// -1 小于 charStr
pub fn compareBuffer(leftStr: []const u8, rightStr: []const u8) Compare {
    const leftLen = leftStr.len;
    const rightLen = rightStr.len;
    if (leftLen == rightLen) {
        for (0..leftLen) |i| {
            if (leftStr[i] > rightStr[i]) {
                return Compare.Greater;
            } else if (leftStr[i] < rightStr[i]) {
                return Compare.Less;
            }
        }
        return Compare.Equal;
    } else if (leftLen > rightLen) {
        for (0..rightLen) |i| {
            if (leftStr[i] > rightStr[i]) {
                return Compare.Greater;
            } else if (leftStr[i] < rightStr[i]) {
                return Compare.Less;
            }
        }
        return Compare.Greater;
    } else {
        for (0..leftLen) |i| {
            if (leftStr[i] > rightStr[i]) {
                return Compare.Greater;
            } else if (leftStr[i] < rightStr[i]) {
                return Compare.Less;
            }
        }
        return Compare.Less;
    }
}

pub fn freeStringArray(strArray: std.ArrayList(String)) void {
    for (strArray.items) |*item| {
        item.deinit();
    }
    strArray.deinit();
}
